package com.springboot.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;

/**
 * BigDecimal运算工具类
 * 1.加减乘除、累加、倍数运算。
 * 2. Integer、Long、Float、Double、String、Object转为BigDecimal。
 * 3. 除法四舍五入并默认保留20位小数点。
 * 4. BigDecimal转为String字符串，并禁止用科学计数法显示结果。
 * 5. 小数点位数保留。
 * 6. 多种数据类型间混合运算（如Double和Long转为BigDecimal运算）
 * 7. 以上所有计算，支持所有入参为空时运算
 * @author GONGZHIJING
 * @since 2020/11/5 14:52
 */
public class BigDecimalUtil {

    /**
     * 加法计算（result = x + y）
     *
     * @param x 被加数（可为null）
     * @param y 加数 （可为null）
     * @return 和 （可为null）
     * @author 大脑补丁 on 2020-03-30 14:52
     */
    public static BigDecimal add(BigDecimal x, BigDecimal y) {
        if (x == null) {
            return y;
        }
        if (y == null) {
            return x;
        }
        return x.add(y);
    }

    /**
     * 加法计算（result = a + b + c + d）
     *
     * @param a 被加数（可为null）
     * @param b 加数（可为null）
     * @param c 加数（可为null）
     * @param d 加数（可为null）
     * @return BigDecimal （可为null）
     * @author 大脑补丁 on 2020-03-30 22:17
     */
    public static BigDecimal add(BigDecimal a, BigDecimal b, BigDecimal c, BigDecimal d) {
        BigDecimal ab = add(a, b);
        BigDecimal cd = add(c, d);
        return add(ab, cd);
    }

    /**
     * 累加计算(result=x + result)
     *
     * @param x      被加数（可为null）
     * @param result 和 （可为null,若被加数不为为null，result默认值为0）
     * @return result 和 （可为null）
     * @author 大脑补丁 on 2020-03-30 14:16
     */
    public static BigDecimal accumulate(BigDecimal x, BigDecimal result) {
        if (x == null) {
            return result;
        }
        if (result == null) {
            result = new BigDecimal("0");
        }
        return result.add(x);
    }

    /**
     * 减法计算(result = x - y)
     *
     * @param x 被减数（可为null）
     * @param y 减数（可为null）
     * @return BigDecimal 差 （可为null）
     * @author 大脑补丁 on 2020-03-30 14:47
     */
    public static BigDecimal subtract(BigDecimal x, BigDecimal y) {
        if (x == null || y == null) {
            return null;
        }
        return x.subtract(y);
    }

    /**
     * 减法计算(result = x - y)
     *  当x、y为null时，按0计算
     * @param x 被减数（可为null）
     * @param y 减数（可为null）
     * @return BigDecimal 差 （可为null）
     * @author 大脑补丁 on 2020-03-30 14:47
     */
    public static BigDecimal subtractNullToZero(BigDecimal x, BigDecimal y) {
        return (x == null ? new BigDecimal(BigInteger.ZERO) : x).subtract((y == null ? new BigDecimal(BigInteger.ZERO) : y));
    }

    /**
     * 乘法计算(result = x × y)
     *
     * @param x 乘数(可为null)
     * @param y 乘数(可为null)
     * @return BigDecimal 积
     * @author 大脑补丁 on 2020-03-30 14:14
     */
    public static BigDecimal multiply(BigDecimal x, BigDecimal y) {
        if (x == null || y == null) {
            return null;
        }
        return x.multiply(y);
    }

    /**
     * 除法计算(result = x ÷ y)
     *
     * @param x 被除数（可为null）
     * @param y 除数（可为null）
     * @return 商 （可为null,四舍五入，默认保留20位小数）
     * @author 大脑补丁 on 2020-03-30 14:15
     */
    public static BigDecimal divide(BigDecimal x, BigDecimal y) {
        if (x == null || y == null || y.compareTo(BigDecimal.ZERO) == 0) {
            return null;
        }
        // 结果为0.000..时，不用科学计数法展示
        return stripTrailingZeros(x.divide(y, 20, BigDecimal.ROUND_HALF_UP));
    }

    /**
     * 转为字符串(防止返回可续计数法表达式)
     *
     * @param x 要转字符串的小数
     * @return String
     * @author 大脑补丁 on 2020-03-30 15:08
     */
    public static String toPlainString(BigDecimal x) {
        if (x == null) {
            return null;
        }
        return x.toPlainString();
    }

    /**
     * 保留小数位数
     *
     * @param x     目标小数
     * @param scale 要保留小数位数
     * @return BigDecimal 结果四舍五入
     * @author 大脑补丁 on 2020-03-30 15:17
     */
    public static BigDecimal scale(BigDecimal x, int scale) {
        if (x == null) {
            return null;
        }
        return x.setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 整型转为BigDecimal
     *
     * @param x(可为null)
     * @return BigDecimal (可为null)
     * @author 大脑补丁 on 2020-03-30 22:24
     */
    public static BigDecimal toBigDecimal(Integer x) {
        if (x == null) {
            return null;
        }
        return new BigDecimal(x.toString());
    }

    /**
     * 长整型转为BigDecimal
     *
     * @param x(可为null)
     * @return BigDecimal (可为null)
     * @author 大脑补丁 on 2020-03-30 22:24
     */
    public static BigDecimal toBigDecimal(Long x) {
        if (x == null) {
            return null;
        }
        return new BigDecimal(x.toString());
    }

    /**
     * 双精度型转为BigDecimal
     *
     * @param x(可为null)
     * @return BigDecimal (可为null)
     * @author 大脑补丁 on 2020-03-30 22:24
     */
    public static BigDecimal toBigDecimal(Double x) {
        if (x == null) {
            return null;
        }
        return new BigDecimal(x.toString());
    }

    /**
     * 单精度型转为BigDecimal
     *
     * @param x(可为null)
     * @return BigDecimal (可为null)
     * @author 大脑补丁 on 2020-03-30 22:24
     */
    public static BigDecimal toBigDecimal(Float x) {
        if (x == null) {
            return null;
        }
        return new BigDecimal(x.toString());
    }

    /**
     * 字符串型转为BigDecimal
     *
     * @param x(可为null)
     * @return BigDecimal (可为null)
     * @author 大脑补丁 on 2020-03-30 22:24
     */
    public static BigDecimal toBigDecimal(String x) {
        if (x == null) {
            return null;
        }
        return new BigDecimal(x);
    }

    /**
     * 对象类型转为BigDecimal
     *
     * @param x(可为null)
     * @return BigDecimal (可为null)
     * @author 大脑补丁 on 2020-03-30 22:24
     */
    public static BigDecimal toBigDecimal(Object x) {
        if (x == null) {
            return null;
        }
        BigDecimal result = null;
        try {
            result = new BigDecimal(x.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 倍数计算，用于单位换算
     *
     * @param x        目标数(可为null)
     * @param multiple 倍数 (可为null)
     * @return BigDecimal (可为null)
     * @author 大脑补丁 on 2020-03-30 22:43
     */
    public static BigDecimal multiple(BigDecimal x, Integer multiple) {
        if (x == null || multiple == null) {
            return null;
        }
        return BigDecimalUtil.multiply(x, toBigDecimal(multiple));
    }

    /**
     * 去除小数点后的0（如: 输入1.000返回1）
     *
     * @param x 目标数(可为null)
     * @return
     */
    public static BigDecimal stripTrailingZeros(BigDecimal x) {
        if (x == null) {
            return null;
        }
        return x.stripTrailingZeros();
    }

    /**
     * 将null转为0
     *
     * @param bigDecimal 目标数(可为null)
     * @return
     */
    public static BigDecimal bigDecimalNullToZero(BigDecimal bigDecimal) {
        if (bigDecimal == null) {
            return BigDecimal.ZERO;
        } else {
            return bigDecimal;
        }
    }

    /**
     * 判断num1是否小于num2
     *
     * @param num1
     * @param num2
     * @return num1小于num2返回true
     */
    public static boolean lessThan(BigDecimal num1, BigDecimal num2) {
        return bigDecimalNullToZero(num1).compareTo(bigDecimalNullToZero(num2)) < 0;
    }

    /**
     * 判断num1是否小于等于num2
     *
     * @param num1
     * @param num2
     * @return num1小于或者等于num2返回true
     */
    public static boolean lessEqual(BigDecimal num1, BigDecimal num2) {
        return (bigDecimalNullToZero(num1).compareTo(bigDecimalNullToZero(num2)) < 0) || (bigDecimalNullToZero(num1).
                compareTo(bigDecimalNullToZero(num2)) == 0);
    }

    /**
     * 判断num1是否大于num2
     *
     * @param num1
     * @param num2
     * @return num1大于num2返回true
     */
    public static boolean greaterThan(BigDecimal num1, BigDecimal num2) {
        return bigDecimalNullToZero(num1).compareTo(bigDecimalNullToZero(num2)) > 0;
    }

    /**
     * 判断num1是否大于等于num2
     *
     * @param num1
     * @param num2
     * @return num1大于或者等于num2返回true
     */
    public static boolean greaterEqual(BigDecimal num1, BigDecimal num2) {
        return (bigDecimalNullToZero(num1).compareTo(bigDecimalNullToZero(num2)) > 0) || (bigDecimalNullToZero(num1)
                .compareTo(bigDecimalNullToZero(num2)) == 0);
    }

    /**
     * 判断num1是否等于num2
     *
     * @param num1
     * @param num2
     * @return num1等于num2返回true
     */
    public static boolean BeEqualTo(BigDecimal num1, BigDecimal num2) {
        return bigDecimalNullToZero(num1).compareTo(bigDecimalNullToZero(num2)) == 0;
    }

    /**
     * 比较两个数字大小，value1 大返回1，value2大返回2，相等返回0，有一项为空返回-2，
     * @param value1
     * @param value2
     * @return
     */
    public static Integer valueCompare(BigDecimal value1, BigDecimal value2){
        if(ObjectUtil.isEmpty(value1)){
            return -2;
        }
        if(ObjectUtil.isEmpty(value2)){
            return -2;
        }
        return value1.compareTo(value2);
    }

    /**
     * string 转数字，异常默认返回 0
     * @param value
     * @return
     */
    public static BigDecimal strToBigDecimalDefault(String value){
        if(StrUtil.isEmpty(value)){
            return BigDecimal.ZERO;
        }
        try{
            return new BigDecimal(value);
        }catch (Exception e){
            return BigDecimal.ZERO;
        }
    }

    /**
     *
     * @param value
     * @return
     */
    public static BigDecimal ifNull(BigDecimal value) {
        if (ObjectUtil.isEmpty(value)) {
            return BigDecimal.ZERO;
        }
        return value;
    }

    /**
     * @description: 空 或者 零
     * @param canDivAmount
     * @return: boolean
     * @author: tanglei
     * @time: 2022/4/2 10:58
     */
    public static boolean isNullOrZero(BigDecimal canDivAmount) {

        if (ObjectUtil.isNull(canDivAmount) ||  BeEqualTo(BigDecimal.ZERO, canDivAmount)) {
            return true;
        }

        return false;
    }

    /**
     * @description:  求和
     * @param list
     * @param mapper
     * @return: java.math.BigDecimal
     * @author: tanglei
     * @time: 2022/8/29 14:44
     */
    public static <T> BigDecimal sumField(List<T> list, Function<? super T, BigDecimal> mapper) {

        if (CollUtil.isEmpty(list)) {
            return BigDecimal.ZERO;
        }
        return list.stream()
                .map(mapper)
                .filter(Objects::nonNull)
                .reduce(BigDecimal::add)
                .orElse(BigDecimal.ZERO);
    }
}
